#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# this lesson is based on python 3

# all we talked below is about synchronous IO 
# asynchronous IO is more efficient but more complex
# ====READ AND WRITE==== 

try: # to avoid IOError of 'no such file', use try...except...finally
    f = open('/path/to/file', 'r') # 'r' == read
    print(f.read()) # .read() return a string
finally:
    if f:
        f.close() # file must be closed after use

with open('/path/to/file', 'r') as f: # this 'with' conditional is equal to try...finally above, need not close
    print(f.read())
    f.readline() # read only one line of content every time
    f.readlines() # return a list of content ordered by line
    for line in f.readlines():
        print(line.strip()) # 把末尾的'\n'删掉

f = open('/Users/michael/test.jpg', 'rb') # 'rb' == read binary (file type other than text)
f.read() # return hex bytes

f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
# text read default by UTF-8, other encoding may be specified, unproper coding errors can be ignored

with open('/Users/michael/test.txt', 'w') as f: # in 'w'=write mode, former text may be overwrite
    f.write('Hello, world!') # system only ensure the input write into file when the file is properly closed
    # to append new text to former file, use 'a' mode of open()


# ====STRINGIO AND BYTESIO==== 

from io import StringIO

sio = StringIO() # StringIO is file-like object
sio.write = ('String')
sio.write = ('IO')
print(sio.getvalue()) # 'StringIO'

sio.readline() # 'StringIO'

sio2 = StringIO('file-like object') # directly initialize a new StringIO object

from io import BytesIO

bio = BytesIO()
bio.write = ('中文'.encode('utf-8')) # write in binary data
print(bio.getvalue()) # b'\xe4\xb8\xad\xe6\x96\x87'

bio.read() # file-like manipulation
bio2 = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87') # directly initialize a new BytesIO object


# ====FILE AND DIRECTORY==== 
import os

os.name
# 'posix' for linux, unix, mac os; 'nt' for windows

os.environ
# a dict of environment variables
os.environ.get('USER') # .get('key')

# 查看当前目录的绝对路径:
os.path.abspath('.')

# 在某个目录下创建一个新目录，首先把新目录的完整路径表示出来:
os.path.join('/Users/michael', 'testdir') # '/Users/michael/testdir'
# 把两个路径合成一个时，不要直接拼字符串，而要通过os.path.join()函数，这样可以正确处理不同操作系统的路径分隔符。

# 同样的道理，要拆分路径时，也不要直接去拆字符串，而要通过os.path.split()函数，
# 这样可以把一个路径拆分为两部分，后一部分总是最后级别的目录或文件名：
os.path.split('/Users/michael/testdir/file.txt') # ('/Users/michael/testdir', 'file.txt')

# os.path.splitext()可以直接让你得到文件扩展名，很多时候非常方便：
os.path.splitext('/path/to/file.txt') # ('/path/to/file', '.txt')

# 然后创建一个目录:
os.mkdir('/Users/michael/testdir')

# 删掉一个目录:
os.rmdir('/Users/michael/testdir')

# 对文件重命名:
os.rename('test.txt', 'test.py')

# 删掉文件:
os.remove('test.py')

# shutil模块提供了copyfile()的函数，你还可以在shutil模块中找到很多实用函数，它们可以看做是os模块的补充。

# 列出当前目录下的所有目录
[x for x in os.listdir('.') if os.path.isdir(x)]
 
# 列出所有的.py文件
[x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py'] 


# ====PICKLING====
# 我们把变量从内存中变成可存储或传输的过程称之为序列化，在Python中叫pickling，
# 在其他语言中也被称之为serialization，marshalling，flattening等等，都是一个意思。
# 序列化之后，就可以把序列化后的内容写入磁盘，或者通过网络传输到别的机器上。
# 反过来，把变量内容从序列化的对象重新读到内存里称之为反序列化，即unpickling。

import pickle

mydict = dict(name = 'Alice', age = 33, score = 77)

myfile = open('dump.txt', 'wb') # write binary
pickle.dump(mydict, myfile) # .dump write a pickled representation of obj to a file-like object.
myfile.close()

myfile = open('dump.txt', 'rb')
newdict = pickle.load(myfile) # Read and return an object from the pickle data stored in a file (unpickling)

# the problem of pickle package is uncompatibility with other platform
# best compatible format for pickling is JSON:
import json

json_string = json.dumps(mydict) # return a pickled string

newdict2 = json.loads(json_string) # unpickle into the same content of mydict

# Python的dict对象可以直接序列化为JSON的{}
# 但是class的实例对象无法序列化为JSON
# 一般可以通过函数把class的实例对象转化为dict，再序列化
def student2dict(std):
    return {
        'name': std.name,
        'age': std.age,
        'score': std.score
    }

class Myclass:
    pass

s = Myclass()

print(json.dumps(s, default=lambda obj: obj.__dict__))